Skip to content

Conversation

@Hexeong
Copy link
Contributor

@Hexeong Hexeong commented Sep 30, 2025

관련 이슈

작업 내용

  • 보드와 카테고리에 따른 포스트 조회 메서드에 대한 정렬 순서 문제 해결 -> createdAt(내림차순) 기준으로 정렬

  • 코멘트 트리 조회 메서드에 대한 정렬 순서 문제 해결 -> path, createdAt 기준으로 정렬

  • 메서드 수정에 따른 기존 테스트 코드 수정(정렬 기준으로 잘 출력되는지 확인)

리뷰 요구사항 (선택)

  • 보드와 카테고리에 따른 포스트 조회 메서드 문제 해결 건과 관련하여 최신 포스트가 최상단에 위치하게 정렬했습니다.

  • 기본 정렬방식이 ASC이기 때문에, DESC 인 경우 메서드 자체에서 표현할 필요가 있다고 느껴져서 메서드 이름에 OrderByCreatedAtDesc를 추가했습니다.

  • 하지만 메서드명이 너무 길어진 부분이 있기 때문에 이 부분에 대해서 의견 남겨주시면 감사드리겠습니다!

@coderabbitai
Copy link

coderabbitai bot commented Sep 30, 2025

Walkthrough

  1. Controller 변경
    • BoardController에서 PostQueryService 호출을 findPostsByCodeAndPostCategoryOrderByCreatedAtDesc(...)로 교체.
  2. Service 변경
    • PostQueryService 공개 메서드명 변경 및 내부 호출을 생성시간 내림차순 정렬 메서드로 통일.
  3. PostRepository 변경
    • findByBoardCodefindByBoardCodeOrderByCreatedAtDesc로 개명.
    • 차단 사용자 제외 쿼리에 ORDER BY p.createdAt DESC 추가 및 메서드명 개명.
  4. CommentRepository 변경
    • 댓글 트리 조회 native 쿼리에 ORDER BY path, created_at 정렬 추가.
  5. 테스트 헬퍼/픽스처 추가
    • TestTimeHelper 추가(Reflection으로 createdAt 설정).
    • Post/Comment 픽스처에 지연 저장 메서드 추가.
  6. 테스트 갱신
    • 정렬 기준 변경에 맞춰 Post/Comment 테스트 시나리오 및 기대값 업데이트.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • wibaek
  • JAEHEE25
  • lsy1307
  • sukangpunch

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning PR 설명은 관련 이슈(#521)와 작업 내용 섹션을 충실히 작성했으나 템플릿에서 요구하는 ‘## 특이 사항’ 섹션이 누락되어 변경사항에 대한 주의사항이나 중요 이슈를 별도로 명시할 공간이 부족합니다. 템플릿에 따라 ‘## 특이 사항’ 섹션을 추가하여 런타임에 주의해야 할 변경점이나 중요한 영향 범위를 명시해주세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed 제목은 PostRepository와 CommentRepository의 일부 메서드에서 발생한 정렬 순서 오류를 해결한다는 PR의 핵심 변경사항을 간결하고 구체적으로 요약하여 불필요한 정보 없이 명확하게 전달하고 있습니다.
Linked Issues Check ✅ Passed 포스트 조회 메서드에 createdAt DESC 정렬 적용, 댓글 트리 조회 메서드에 path 및 createdAt 정렬 구현, 연관 서비스·컨트롤러 호출 변경과 테스트 보강 등 이슈 #521에서 제시한 모든 코딩 요구사항이 충실히 반영되었습니다.
Out of Scope Changes Check ✅ Passed 메서드명 변경, 서비스·컨트롤러 호출 수정, 테스트 픽스처 및 헬퍼 클래스 추가 등 모든 변경사항이 정렬 오류 해결 및 검증 범위에 국한되어 있어 이슈 범위를 벗어나는 코드는 포함되지 않았습니다.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java (1)

85-91: 테스트 커버리지 강화 확인

  1. 테스트명 개선: "생성시간 기준으로 정렬해"를 추가하여 테스트 의도를 명확히 했습니다.
  2. 테스트 데이터 확대: 단일 자식 댓글에서 3개의 자식 댓글로 확장하여 정렬 동작을 더 잘 검증할 수 있게 되었습니다.

다만 현재 테스트는 각 댓글의 존재 여부는 확인하지만, 실제 반환 순서는 검증하지 않습니다(lines 109-133에서 filteredOn 사용). CommentRepository의 ORDER BY path, created_at 변경사항을 완전히 검증하려면 다음과 같은 순서 검증을 추가하는 것을 고려해보세요:

() -> assertThat(responses)
    .extracting(PostFindCommentResponse::id)
    .containsExactly(
        parentComment.getId(),
        childComment1.getId(),
        childComment2.getId(),
        childComment3.getId()
    )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 17aa696 and 00f2372.

📒 Files selected for processing (6)
  • src/main/java/com/example/solidconnection/community/board/controller/BoardController.java (1 hunks)
  • src/main/java/com/example/solidconnection/community/comment/repository/CommentRepository.java (1 hunks)
  • src/main/java/com/example/solidconnection/community/post/repository/PostRepository.java (1 hunks)
  • src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java (1 hunks)
  • src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java (2 hunks)
  • src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java (5 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (8)
src/main/java/com/example/solidconnection/community/board/controller/BoardController.java (1)

39-40: 변경사항 확인 완료

서비스 메서드명을 findPostsByCodeAndPostCategoryOrderByCreatedAtDesc로 업데이트하여 생성일 내림차순 정렬을 명확히 표현했습니다. 메서드 시그니처와 파라미터가 일관되게 유지되고 있어 좋습니다.

src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java (1)

108-112: 메서드명 업데이트 확인

새로운 메서드명 findPostsByCodeAndPostCategoryOrderByCreatedAtDesc를 사용하도록 일관되게 변경되었습니다.

src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java (2)

49-49: 메서드명 변경으로 정렬 의도 명확화

메서드명에 OrderByCreatedAtDesc를 추가하여 생성일 내림차순 정렬을 명시적으로 표현했습니다. API 사용자가 정렬 순서를 쉽게 파악할 수 있어 좋은 개선입니다.

다만 PR 설명에서 언급하신 것처럼, 메서드명이 다소 길어진 점은 트레이드오프가 있습니다. 현재 방식이 명확성 측면에서는 우수하지만, 팀 내에서 다음 대안도 검토해볼 수 있습니다:

  1. 현재 방식 유지 (권장): 명시적이고 오해의 여지가 없음
  2. @orderby 애노테이션 활용: 기본 정렬을 엔티티 레벨에서 정의
  3. 기본 메서드를 DESC로, ASC가 필요한 경우만 별도 메서드: 대부분의 목록 조회가 최신순이라면 이것이 더 간결할 수 있음

57-60: 레포지토리 메서드 호출 업데이트 확인

차단된 사용자 필터링 여부에 따라 적절한 DESC 정렬 메서드를 호출하도록 업데이트되었습니다. 로직이 명확하고 정렬 보장이 잘 반영되어 있습니다.

src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java (1)

109-133: 자식 댓글 개별 검증 로직 확인

각 자식 댓글(childComment1, childComment2, childComment3)의 id, parentId, isOwner를 개별적으로 검증하도록 확장했습니다. 모든 댓글이 올바른 속성을 가지고 있는지 꼼꼼하게 확인하고 있어 좋습니다.

src/main/java/com/example/solidconnection/community/comment/repository/CommentRepository.java (1)

37-37: 정렬 기준에 created_at 추가 검토 요청

  1. Path 문자열 정렬 문제
    댓글 경로를 CAST(id AS CHAR(255))로 생성하면 ID가 문자열로 사전식 정렬되어 의도와 다른 순서가 나올 수 있습니다.
  2. ORDER BY 개선 제안
    LPAD(id, 10, '0') 같은 고정 길이 패딩 또는 ORDER BY에서 숫자 기준 파싱 방식을 도입해보세요.
  3. 추가 검증 요청
    데이터에 ID ≥ 10인 댓글이 존재하는지 아래 예시 명령어로 확인하고 결과를 알려주세요.
    # MySQL 예시
    mysql -u<user> -p<pass> -e "SELECT id FROM comment WHERE id >= 10 LIMIT 1;"
src/main/java/com/example/solidconnection/community/post/repository/PostRepository.java (2)

17-17: 변경사항 1. Spring Data JPA 네이밍 규칙을 활용한 정렬 보장

메서드명에 OrderByCreatedAtDesc를 추가하여 Spring Data JPA가 자동으로 생성일 기준 내림차순 정렬 쿼리를 생성하도록 했습니다. 이는 Spring Data의 관례를 따르는 깔끔한 방식입니다.


19-27: 변경사항 검증 및 승인

  1. 커스텀 쿼리 정렬 추가
    • ORDER BY p.createdAt DESC를 통해 최신 게시글 우선 조회 보장 (라인 25).
    • 메서드명 findByBoardCodeExcludingBlockedUsersOrderByCreatedAtDesc로 정렬 의도 명시 (라인 27).
  2. 기존 메서드명 호출 제거 검증 완료
    • src 디렉토리 전체 rg 검색 결과 findByBoardCode* 호출 없음.

모든 검증을 마쳤으며 변경사항을 승인합니다.

@Hexeong Hexeong added the 버그 Something isn't working label Sep 30, 2025
@Hexeong Hexeong self-assigned this Sep 30, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/main/java/com/example/solidconnection/common/BaseEntity.java (1)

11-11: 중복 임포트 정리 권장

Line 4에서 ChronoUnit.MICROS를 이미 정적 임포트했으므로, Line 11의 비정적 임포트는 불필요합니다. Line 40에서 ChronoUnit.MICROS 대신 MICROS를 사용하면 정적 임포트만으로 충분합니다.

다음과 같이 정리할 수 있습니다:

-import java.time.temporal.ChronoUnit;
-
 import lombok.Getter;

그리고 Line 40을:

-    public void setCreatedAt(ZonedDateTime createdAt) {
-        this.createdAt = createdAt.truncatedTo(ChronoUnit.MICROS);
-    }
+    public void setCreatedAt(ZonedDateTime createdAt) {
+        this.createdAt = createdAt.truncatedTo(MICROS);
+    }
src/test/java/com/example/solidconnection/community/post/fixture/PostFixtureBuilder.java (1)

81-92: 코드 중복 제거 및 매개변수 명명 개선 권장

  1. 코드 중복:

    • Lines 82-89는 Lines 70-77과 동일합니다.
    • create() 메서드를 private 헬퍼로 추출하고 두 public 메서드에서 재사용하는 것을 권장합니다.
  2. 매개변수 명명:

    • int time은 단위가 불명확합니다. Line 90에서 plusSeconds(time)로 사용되므로 int secondsOffset 또는 int delaySeconds가 더 명확합니다.

다음과 같이 리팩토링할 수 있습니다:

-    public Post create() {
+    private Post buildPost() {
         Post post = new Post(
                 title,
                 content,
                 isQuestion,
                 likeCount,
                 viewCount,
                 postCategory);
         post.setBoardAndSiteUserId(board.getCode(), siteUser.getId());
+        return post;
+    }
+
+    public Post create() {
+        Post post = buildPost();
         return postRepository.save(post);
     }
 
-    public Post createAfterCertainTime(int time) {
-        Post post = new Post(
-                title,
-                content,
-                isQuestion,
-                likeCount,
-                viewCount,
-                postCategory);
-        post.setBoardAndSiteUserId(board.getCode(), siteUser.getId());
-        post.setCreatedAt(ZonedDateTime.now(ZoneOffset.UTC).plusSeconds(time));
+    public Post createAfterCertainTime(int delaySeconds) {
+        Post post = buildPost();
+        post.setCreatedAt(ZonedDateTime.now(ZoneOffset.UTC).plusSeconds(delaySeconds));
         return postRepository.save(post);
     }
src/test/java/com/example/solidconnection/community/post/fixture/PostFixture.java (1)

51-70: 매개변수 명명 일관성 개선 권장

Line 58의 int time 매개변수는 PostFixtureBuilder의 동일한 문제를 상속합니다. PostFixtureBuilder에서 제안한 대로 int delaySeconds로 변경하면 사용 의도가 명확해집니다.

 public Post 게시글_특정_시간_이후로_저장(
         String title,
         String content,
         Boolean isQuestion,
         PostCategory postCategory,
         Board board,
         SiteUser siteUser,
-        int time
+        int delaySeconds
 ) {
     return postFixtureBuilder
             .title(title)
             .content(content)
             .isQuestion(isQuestion)
             .likeCount(0L)
             .viewCount(0L)
             .postCategory(postCategory)
             .board(board)
             .siteUser(siteUser)
-            .createAfterCertainTime(time);
+            .createAfterCertainTime(delaySeconds);
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5540b44 and 8c100d9.

📒 Files selected for processing (4)
  • src/main/java/com/example/solidconnection/common/BaseEntity.java (2 hunks)
  • src/test/java/com/example/solidconnection/community/post/fixture/PostFixture.java (1 hunks)
  • src/test/java/com/example/solidconnection/community/post/fixture/PostFixtureBuilder.java (2 hunks)
  • src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java (5 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (3)
src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java (3)

79-96: 시간 기반 테스트 데이터 생성 개선 확인

이전 리뷰 코멘트에서 제기된 타임스탬프 비결정성 문제가 해결되었습니다:

  1. 명시적 타임스탬프 설정:

    • post1은 기본 @PrePersist로 현재 시각 사용
    • post2는 now+3초, post3는 now+6초로 설정하여 명확한 시간 순서 보장
  2. 충분한 시간 간격:

    • 3초 간격은 마이크로초 정밀도를 고려해도 충분히 안정적입니다.
  3. 테스트 결정성:

    • Line 102의 List.of(post3, post2, post1) 순서가 이제 확정적으로 검증됩니다.

100-121: 정렬 순서 검증 로직 확인

  1. 기대 결과 순서:

    • Line 102의 List.of(post3, post2, post1)은 createdAt DESC 정렬을 올바르게 반영합니다.
  2. 메서드명 개선:

    • Line 110의 findPostsByCodeAndPostCategoryOrderByCreatedAtDesc는 정렬 기준을 명시적으로 표현합니다.
  3. 테스트 커버리지:

    • 카테고리 필터링 + 정렬 순서를 함께 검증하여 통합 동작을 확인합니다.

123-260: 나머지 테스트 메서드 업데이트 확인

  1. 일관된 메서드명 변경:

    • Lines 133, 205, 223, 249 모두 findPostsByCodeAndPostCategoryOrderByCreatedAtDesc로 통일되었습니다.
  2. 정렬 순서 일관성:

    • Lines 126, 102 등 모든 기대 결과가 [post3, post2, post1] 순서를 유지합니다.
  3. 기능 테스트 커버리지:

    • 전체 카테고리 조회 (Lines 123-144)
    • 썸네일 조회 (Lines 197-236)
    • 차단 사용자 필터링 (Lines 239-260)
    • 모두 정렬 기준 변경과 함께 검증됩니다.

Comment on lines 39 to 41
public void setCreatedAt(ZonedDateTime createdAt) {
this.createdAt = createdAt.truncatedTo(ChronoUnit.MICROS);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

null 안전성 및 가시성 검토 필요

  1. null 안전성 문제:

    • Line 40에서 createdAt.truncatedTo()를 호출하는데, createdAt이 null인 경우 NPE가 발생합니다.
    • 입력 검증 또는 null 체크가 필요합니다.
  2. public 가시성 위험:

    • 감사 타임스탬프를 외부에서 변경 가능하게 하면 데이터 무결성 문제가 발생할 수 있습니다.
    • 테스트 픽스처 용도로만 사용된다면 package-private 접근 제어나 @VisibleForTesting 어노테이션을 고려하세요.

다음 스크립트로 프로덕션 코드에서 이 setter의 사용 여부를 확인하세요:

null 안전성을 위한 수정 제안:

 public void setCreatedAt(ZonedDateTime createdAt) {
+    if (createdAt == null) {
+        throw new IllegalArgumentException("createdAt cannot be null");
+    }
     this.createdAt = createdAt.truncatedTo(MICROS);
 }

🏁 Script executed:

#!/bin/bash
# Description: Verify setCreatedAt is only used in test code

echo "=== Production code usage (should be minimal/none) ==="
rg -n "\.setCreatedAt\(" --type=java -g '!**/test/**' -C 2

echo ""
echo "=== Test code usage ==="
rg -n "\.setCreatedAt\(" --type=java -g '**/test/**' -C 2

Length of output: 918


변경 제안: setCreatedAt 메서드 null 검증 및 가시성 강화

  1. null 검증 추가

    public void setCreatedAt(ZonedDateTime createdAt) {
    +   if (createdAt == null) {
    +       throw new IllegalArgumentException("createdAt는 null일 수 없습니다");
    +   }
        this.createdAt = createdAt.truncatedTo(ChronoUnit.MICROS);
    }
  2. @VisibleForTesting 어노테이션 적용

    • Guava 또는 프로젝트 내 @VisibleForTesting 어노테이션을 import 후 메서드에 추가해 테스트 전용임을 명시
  3. 프로덕션 코드 사용 여부 재확인 스크립트

    # src/main/java 디렉토리 내에서만 검색해 호출 존재 여부 확인
    rg -n "\.setCreatedAt\(" src/main/java -C2
🤖 Prompt for AI Agents
In src/main/java/com/example/solidconnection/common/BaseEntity.java around lines
39 to 41, the setCreatedAt method should be hardened: add a null check and only
call truncatedTo when createdAt is non-null (e.g., if (createdAt == null) {
this.createdAt = null; return; } this.createdAt =
createdAt.truncatedTo(ChronoUnit.MICROS);), annotate the method with
@VisibleForTesting (import Guava or your project’s VisibleForTesting) to mark it
as test-only, and run a usage check to ensure it’s not used in production code
(e.g., rg -n "\.setCreatedAt\(" src/main/java -C2) before committing.

Copy link
Contributor

@Gyuhyeok99 Gyuhyeok99 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

첫 pr 축하드립니다~~ 🎉

전반적으로 다 잘 작성해주신 거 같습니다! 이건 변경사항이 적어도 pr을 나눠도 좋았겠네요!

하지만 메서드명이 너무 길어진 부분이 있기 때문에 이 부분에 대해서 의견 남겨주시면 감사드리겠습니다!

저는 작성해주신 함수명이 JPA에도 맞고 직관적이어서 잘 추가해주셨다고 생각합니다!

밑에 작성되어있겠지만 저는 테스트 코드를 위해 프로덕션 코드를 수정하는 걸 지양하는 편입니다! 확인 한 번 부탁드려요~

추가로 작업하신 파일들은 커밋 전에 저희 템플릿 활용해서 Reformat Code 적용 한 번씩 부탁드립니다! 어려우시면 디스코드로 멘션 주세요!

마지막으로 커밋 제목 작성 시 commitType:(공백)메시지 입니다!

as is

commitType(공백):(공백)메시지

to be

commitType:(공백)메시지

스쿼시 머지할 때 확인 부탁드려요~ 고생 많으셨습니다!!

Comment on lines 39 to 41
public void setCreatedAt(ZonedDateTime createdAt) {
this.createdAt = createdAt.truncatedTo(ChronoUnit.MICROS);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 테스트 코드를 위해 프로덕션 코드를 수정하는 방향으로 가는 건 좋지 않다고 생각합니다!

간단히 생각나는 건
Thread.sleep() 등으로 실제 시간 차이를 두는 게 가장 간단할 거 같고 아니면 리플렉션 쓰는 방식도 있을 거 같은데 이건 저도 잘 안써보긴 했습니다!

한번 고민해보시는 것도 좋을 거 같아요!

@coderabbitai

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Gyuhyeok99 훌륭한 지적입니다! 테스트를 위해 프로덕션 코드에 public setter를 추가하는 것은 좋지 않은 접근 방식입니다.

현재 접근 방식의 문제점:

  • BaseEntitycreatedAt 캡슐화가 깨짐
  • 프로덕션 환경에서 의도치 않은 타임스탬프 수정 가능성
  • JPA 라이프사이클(@PrePersist)과 충돌 가능성

더 나은 대안들:

  1. Reflection 사용 (추천):

    // 테스트 유틸리티 클래스
    public class TestTimeHelper {
        public static void setCreatedAt(BaseEntity entity, ZonedDateTime time) {
            try {
                Field field = BaseEntity.class.getDeclaredField("createdAt");
                field.setAccessible(true);
                field.set(entity, time.truncatedTo(ChronoUnit.MICROS));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
  2. @TestConfiguration으로 테스트용 EntityManager 활용:

    // 테스트에서 EntityManager로 직접 필드 업데이트
    entityManager.createQuery("UPDATE Post p SET p.createdAt = :time WHERE p.id = :id")
        .setParameter("time", specificTime)
        .setParameter("id", post.getId())
        .executeUpdate();
  3. Thread.sleep() (간단하지만 테스트 속도 저하):

    • 밀리초 단위로도 충분하므로 Thread.sleep(10) 정도면 됩니다

저의 추천: Reflection을 사용한 테스트 헬퍼 클래스를 만드는 것이 가장 깔끔합니다. 테스트 코드에서만 사용되고, 프로덕션 코드의 캡슐화를 유지하며, 테스트 실행 속도도 빠릅니다.

프로덕션 코드에서 setCreatedAt() 메서드를 제거하고 reflection 기반 접근 방식으로 변경하시겠습니까?

(\\_/)
(•‿•)  
/>📚 더 나은 설계를 위해 고민 중...

✏️ Learnings added
Learnt from: Gyuhyeok99
PR: solid-connection/solid-connect-server#522
File: src/main/java/com/example/solidconnection/common/BaseEntity.java:39-41
Timestamp: 2025-09-30T09:16:14.959Z
Learning: BaseEntity와 같은 엔티티의 타임스탬프 필드는 테스트를 위해 public setter를 추가하지 말고, reflection이나 EntityManager를 활용한 테스트 전용 헬퍼를 사용해야 합니다. 프로덕션 코드의 캡슐화를 유지하는 것이 중요합니다.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

생각을 해봤을 때, 저도 reflaction 방식으로 TestTimeHelper 클래스를 통해 직접 지정해주는 방식이 나은것 같네요 ㅎㅎ 이것도 반영해보겠습니다!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Contributor

@Gyuhyeok99 Gyuhyeok99 Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네! 편한 대로 해보셔도 좋을 거 같습니다~ 근데 너무 복잡하실 거 같으면 밑에 말처럼 그냥 Thread.sleep(10) 써도 되겠네요!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai
근데 이거 그냥 createdAt 변경 없이 @PrePersist에서 createdAt을 마이크로초 단위로 잘라 자동 설정해도 문제 없는 거 같기도 한데 테스트 깨지는 경우가 있긴 해?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Gyuhyeok99 좋은 질문입니다!

@PrePersist만으로 충분한지 여부:

실제로 @PrePersist만으로도 대부분의 경우 문제없이 동작할 수 있습니다. 하지만 테스트에서 정렬 순서를 검증할 때 문제가 발생할 수 있습니다:

1. 테스트 환경에서의 타이밍 이슈:

  • 현재 테스트 코드에서 post1, post2, post3을 순차적으로 저장할 때, JVM과 DB의 처리 속도가 매우 빠르면 동일한 마이크로초 타임스탬프가 부여될 수 있습니다
  • 마이크로초(microsecond) 정밀도라도 최신 하드웨어에서는 여러 INSERT가 1마이크로초 안에 완료될 수 있습니다
  • 타임스탬프가 동일하면 정렬 순서가 **비결정적(non-deterministic)**이 되어 테스트가 간헐적으로 실패할 수 있습니다

2. 현재 PR의 테스트 코드 구조:

// PostFixtureBuilder.createAfterCertainTime(int time)에서
post.setCreatedAt(ZonedDateTime.now(ZoneOffset.UTC).plusSeconds(time));
  • 이 코드는 의도적으로 시간 차이를 두어 정렬 순서를 명확하게 제어하려는 의도입니다
  • @PrePersist만 사용하면 이 plusSeconds(time) 로직이 무시되고 저장 시점의 현재 시간으로 덮어써집니다

3. 더 나은 대안:

실제로 Thread.sleep(10)을 사용하는 방식이 가장 간단하고 실용적일 수 있습니다:

// PostFixture
public Post 게시글_특정_시간_이후로_저장(...) {
    try {
        Thread.sleep(10); // 10ms 대기
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
    return builder.create(); // @PrePersist가 자동으로 createdAt 설정
}

결론:

  • @PrePersist만으로도 프로덕션에서는 전혀 문제없습니다
  • 하지만 테스트에서 정렬 순서 검증을 위해서는:
    • Thread.sleep()으로 실제 시간 차이 두기 (가장 간단)
    • 또는 reflection으로 명시적 시간 설정 (더 정교한 제어)

저는 Thread.sleep(10) 방식을 추천합니다. Reflection보다 코드가 명확하고, 10ms 정도는 테스트 속도에 큰 영향을 주지 않습니다!

(\\_/)
(•ᴗ•)  
/>⏱️ 간단한 게 최고!

🧠 Learnings used
Learnt from: Gyuhyeok99
PR: solid-connection/solid-connect-server#522
File: src/main/java/com/example/solidconnection/common/BaseEntity.java:39-41
Timestamp: 2025-09-30T09:16:14.959Z
Learning: BaseEntity와 같은 엔티티의 타임스탬프 필드는 테스트를 위해 public setter를 추가하지 말고, reflection이나 EntityManager를 활용한 테스트 전용 헬퍼를 사용해야 합니다. 프로덕션 코드의 캡슐화를 유지하는 것이 중요합니다.

)
SELECT * FROM CommentTree
ORDER BY path
ORDER BY path, created_at
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

전 이 path가 문자열이라서 사전식 정렬로 인한 문제가 된다고 늘 생각했었는데 이렇게 간단하게 해결이 되는군요!

Copy link
Contributor

@Gyuhyeok99 Gyuhyeok99 Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

차단 유저가 있는 경우에도 이 순서가 잘 지켜지는 테스트 코드가 있으면 좋겠습니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

추가해보겠습니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영했습니다!

Comment on lines 111 to 129
() -> assertThat(response.id()).isEqualTo(childComment1.getId()),
() -> assertThat(response.parentId()).isEqualTo(parentComment.getId()),
() -> assertThat(response.isOwner()).isFalse()
)),
() -> assertThat(responses)
.filteredOn(response -> response.id().equals(childComment2.getId()))
.singleElement()
.satisfies(response -> assertAll(
() -> assertThat(response.id()).isEqualTo(childComment2.getId()),
() -> assertThat(response.parentId()).isEqualTo(parentComment.getId()),
() -> assertThat(response.isOwner()).isTrue()
)),
() -> assertThat(responses)
.filteredOn(response -> response.id().equals(childComment3.getId()))
.singleElement()
.satisfies(response -> assertAll(
() -> assertThat(response.id()).isEqualTo(childComment3.getId()),
Copy link
Contributor

@Gyuhyeok99 Gyuhyeok99 Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 순서 관련 검증이 없는 거 같은데 맞나요?
filteredOn은 순서와 상관 없는 거 같습니다.

별도로

assertThat(responses)
        .extracting("id")
        .containsExactly(
            parent.getId(),
            child1.getId(),
            child2.getId(),
            child3.getId()
        );

이런식으로 테스트 하나 더 해도 좋을 거 같습니다~

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai
이 의견도 확인해줘

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Gyuhyeok99 네, 맞습니다! 좋은 지적입니다. 👍

현재 테스트는 filteredOn을 사용해서 각 댓글의 존재 여부와 속성만 검증하고 있고, 실제 정렬 순서는 검증하지 않고 있습니다.

테스트 이름이 "게시글의_모든_댓글과_대댓글을_생성시간_기준으로_정렬해_조회한다"인 만큼, 정렬 순서를 명시적으로 검증하는 assertion을 추가하는 것이 좋겠습니다.

제안하신 방식대로 추가하면 됩니다:

assertThat(responses)
        .extracting("id")
        .containsExactly(
            parentComment.getId(),
            childComment1.getId(),
            childComment2.getId(),
            childComment3.getId()
        );

이렇게 하면 댓글 트리가 path와 createdAt 기준으로 올바르게 정렬되었는지 명확하게 검증할 수 있습니다. 현재 PR의 핵심 수정 사항이 정렬 순서 문제 해결이므로, 이런 검증은 꼭 필요합니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기존 코드 그대로 활용하려다가 신경을 못썼었나보네요 반영해보겠습니다!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영했습니다!

Comment on lines 51 to 70
public Post 게시글_특정_시간_이후로_저장(
String title,
String content,
Boolean isQuestion,
PostCategory postCategory,
Board board,
SiteUser siteUser,
int time
) {
return postFixtureBuilder
.title(title)
.content(content)
.isQuestion(isQuestion)
.likeCount(0L)
.viewCount(0L)
.postCategory(postCategory)
.board(board)
.siteUser(siteUser)
.createAfterCertainTime(time);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위에 말씀드린 리뷰를 반영한다면 이것도 크게 필요 없지 않을까 생각이 듭니다~

Copy link
Contributor Author

@Hexeong Hexeong Oct 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reflaction으로 comment 엔티티를 저장한 후 다시 createdAt 필드를 고쳐서 적용하는 방식을 사용하다보니 해당 부분에 대해서는 아래와 같이 코드가 생길 것 같아요! 하지만 Production 코드에는 영향이 안가니 괜찮은 느낌이 드는데 의견 부탁드립니다!

// CommentFixture.java
public Comment 자식_댓글_지연저장(
            String content,
            Post post,
            SiteUser siteUser,
            Comment parentComment,
            long secondsDelay
    ) {
        return commentFixtureBuilder
                .content(content)
                .post(post)
                .siteUser(siteUser)
                .parentComment(parentComment)
                .createChildWithDelaySeconds(secondsDelay);
    }


// CommentFixtureBuilder.java
public Comment createChildWithDelaySeconds(long seconds) {
        Comment comment = new Comment(content);
        comment.setPostAndSiteUserId(post, siteUser.getId());
        comment.setParentCommentAndPostAndSiteUserId(parentComment, post, siteUser.getId());

        Comment saved = commentRepository.save(comment);

        TestTimeHelper.setCreatedAt(saved, saved.getCreatedAt().plusSeconds(seconds));

        return commentRepository.save(saved);
    }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 fixture는 테스트 코드를 위한 클래스들이니 자유롭게 추가해도 괜찮습니다~

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 반영했습니다!

Comment on lines 80 to 95

public Post createAfterCertainTime(int time) {
Post post = new Post(
title,
content,
isQuestion,
likeCount,
viewCount,
postCategory);
post.setBoardAndSiteUserId(board.getCode(), siteUser.getId());
post.setCreatedAt(ZonedDateTime.now(ZoneOffset.UTC).plusSeconds(time));
return postRepository.save(post);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이것두요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 반영했습니다!

}

@Test
void 게시판_코드와_카테고리로_게시글_목록을_조회한다() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테스트이름에도 최신순으로_조회한다 등으로 바꾸면 어떨까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영했습니다~

.isEqualTo(expectedResponses);
}

@Test
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기도요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 이부분도 순서에 대한 검증로직을 추가하겠습니다!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여긴 네이밍 말씀드린 것이긴 했습니다! 위치가 제대로 안찍혔네요

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그래도 일단 order by 절을 추가한게 맞으니 순서 검증에 대한 로직이 포함되면 좋을 것 같아서 일단 반영했습니다!

Hexeong added 14 commits October 1, 2025 16:23
findByBoardCodeExcludingBlockedUsers ->
findByBoardCodeExcludingBlockedUsersOrderByCreatedAtDesc
변경된 메서드:
- 게시글의_모든_댓글과_대댓글을_생성시간_기준으로_정렬해_조회한다
변경된 메서드:
- 게시판_코드와_카테고리로_게시글_목록을_최신순으로_조회한다
@Hexeong Hexeong force-pushed the fix/521-community-query-order branch from 8c100d9 to 6edde9b Compare October 1, 2025 07:37
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java (1)

141-159: [버그] 전체 카테고리 기대값에서 post4 누락으로 실패 가능.

    1. BoardCode.FREE 기준이면 post4(자유게시판)도 포함되어야 합니다.
    1. 현재 expectedPosts 계산에서 post4를 아예 후보군에 넣지 않아 실제 결과와 불일치합니다.

다음 수정으로 해결하세요.

-        List<Post> posts = List.of(post3, post2, post1);
+        List<Post> posts = List.of(post4, post3, post2, post1);

선택: 정렬 검증을 강화하려면 아래도 추가하세요.

         assertThat(actualResponses)
                 .usingRecursiveComparison()
                 .ignoringFieldsOfTypes(ZonedDateTime.class)
                 .isEqualTo(expectedResponses);
+        assertThat(actualResponses).extracting(PostListResponse::id)
+                .containsExactly(post4.getId(), post3.getId(), post1.getId());
♻️ Duplicate comments (1)
src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java (1)

96-97: 디버그 코드 재등장 - 다시 제거 필요합니다.

System.out.println(responses); 코드가 이전 커밋(44ae338~5540b44)에서 제거되었으나 다시 추가되었습니다. 테스트 코드에서 표준 출력은 CI 로그를 오염시키므로 제거해주세요.

-            System.out.println(responses);
-
🧹 Nitpick comments (8)
src/test/java/com/example/solidconnection/common/helper/TestTimeHelper.java (2)

15-17: 예외 처리를 개선하여 디버깅을 용이하게 해주세요.

현재 모든 예외를 RuntimeException으로 감싸면서 원래 예외 메시지가 손실됩니다. 테스트 실패 시 디버깅을 돕기 위해 더 구체적인 메시지를 추가하는 것을 권장합니다.

다음과 같이 개선할 수 있습니다:

         } catch (Exception e) {
-            throw new RuntimeException(e);
+            throw new RuntimeException("Failed to set createdAt on BaseEntity: " + e.getMessage(), e);
         }

12-14: 선택사항: 필드 접근성을 원래대로 복원하는 것을 고려해보세요.

setAccessible(true) 호출 후 필드를 다시 setAccessible(false)로 되돌리지 않지만, 테스트 환경에서는 큰 문제가 되지 않습니다. 다만 더 엄격한 격리가 필요하다면 finally 블록에서 복원하는 것도 방법입니다.

더 엄격한 격리가 필요한 경우:

 public static void setCreatedAt(BaseEntity entity, ZonedDateTime time) {
+    Field field = null;
     try {
-        Field field = BaseEntity.class.getDeclaredField("createdAt");
+        field = BaseEntity.class.getDeclaredField("createdAt");
         field.setAccessible(true);
         field.set(entity, time.truncatedTo(ChronoUnit.MICROS));
     } catch (Exception e) {
         throw new RuntimeException("Failed to set createdAt on BaseEntity: " + e.getMessage(), e);
+    } finally {
+        if (field != null) {
+            field.setAccessible(false);
+        }
     }
 }
src/test/java/com/example/solidconnection/community/comment/fixture/CommentFixtureBuilder.java (1)

56-58: 선택사항: 코드 중복을 제거하여 유지보수성을 높여보세요.

Lines 56-58은 createChild() 메서드의 Lines 49-51과 동일합니다. 공통 로직을 추출하면 향후 변경 시 일관성을 유지하기 쉬워집니다.

다음과 같이 리팩토링할 수 있습니다:

     public Comment createChild() {
+        return saveChild();
+    }
+
+    public Comment createChildWithDelaySeconds(long seconds) {
+        Comment saved = saveChild();
+        TestTimeHelper.setCreatedAt(saved, saved.getCreatedAt().plusSeconds(seconds));
+        return commentRepository.save(saved);
+    }
+
+    private Comment saveChild() {
         Comment comment = new Comment(content);
         comment.setPostAndSiteUserId(post, siteUser.getId());
         comment.setParentCommentAndPostAndSiteUserId(parentComment, post, siteUser.getId());
         return commentRepository.save(comment);
     }
-
-    public Comment createChildWithDelaySeconds(long seconds) {
-        Comment comment = new Comment(content);
-        comment.setPostAndSiteUserId(post, siteUser.getId());
-        comment.setParentCommentAndPostAndSiteUserId(parentComment, post, siteUser.getId());
-
-        Comment saved = commentRepository.save(comment);
-
-        TestTimeHelper.setCreatedAt(saved, saved.getCreatedAt().plusSeconds(seconds));
-
-        return commentRepository.save(saved);
-    }
src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java (1)

109-135: 최신순(내림차순) 시나리오 검증이 명확합니다.

    1. 메서드명·설명 모두 최신순 의도를 잘 드러냅니다.
    1. ID 순서까지 containsExactly로 보강한 점이 좋습니다. 다만 createdAt 자체의 내림차순을 직접 검증하는 보조 어서션을 고려해보세요.

다음과 같이 추후 보강을 제안드립니다(필드가 노출된다면):

assertThat(actualResponses)
    .extracting(PostListResponse::createdAt)
    .isSortedAccordingTo(Comparator.reverseOrder());
src/main/java/com/example/solidconnection/community/post/repository/PostRepository.java (2)

17-17: 파생 쿼리로 최신순을 명시한 선택, 다만 페이지네이션도 고려해주세요.

    1. 대량 데이터에서 전량 List 반환은 부담이 큽니다.
    1. Pageable/Sort를 받아 확장성 있게 가는 편이 안전합니다.

예시:

Page<Post> findByBoardCodeOrderByCreatedAtDesc(String boardCode, Pageable pageable);
// 또는
List<Post> findByBoardCode(String boardCode, Sort sort);

서비스에서는 Sort.by(DESC, "createdAt") 혹은 Pageable.ofSize(n)로 제어하세요.


19-27: NOT IN → NOT EXISTS로 NULL/성능 리스크 완화 제안.

    1. NOT IN 서브쿼리에 NULL이 섞일 경우 3VL로 인해 예외적 동작을 유발할 수 있습니다.
    1. NOT EXISTS 패턴이 인덱스 활용·카디널리티 추정에 유리한 경우가 많습니다.
-    @Query("""
-       SELECT p FROM Post p
-       WHERE p.boardCode = :boardCode
-       AND p.siteUserId NOT IN (
-           SELECT ub.blockedId FROM UserBlock ub WHERE ub.blockerId = :siteUserId
-       )
-       ORDER BY p.createdAt DESC
-       """)
+    @Query("""
+       SELECT p FROM Post p
+       WHERE p.boardCode = :boardCode
+         AND NOT EXISTS (
+           SELECT 1 FROM UserBlock ub
+           WHERE ub.blockerId = :siteUserId
+             AND ub.blockedId = p.siteUserId
+         )
+       ORDER BY p.createdAt DESC
+       """)
    List<Post> findByBoardCodeExcludingBlockedUsersOrderByCreatedAtDesc(@Param("boardCode") String boardCode, @Param("siteUserId") Long siteUserId);

추가 제안:

    1. 목록 화면 N+1 완화: 썸네일 산출에 postImageList가 필요하다면 @entitygraph(attributePaths = "postImageList")를 본 조회에도 고려하세요.
    1. 운영 팁: 인덱스 (boardCode, createdAt DESC), (UserBlock.blockerId, blockedId) 추천입니다.
src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java (2)

49-62: 정렬 보장은 서비스에서 확실해졌습니다. 다만 필터링을 DB로 내리면 더 안전합니다.

    1. 최신순 보장을 위해 정렬된 리포지토리를 호출한 점은 적절합니다.
    1. 카테고리 필터를 메모리에서 하지 말고 DB로 위임하면 불필요 로우 로딩을 줄일 수 있습니다.

리포지토리에 다음 시그니처를 추가하고 여기서 분기 호출하세요.

List<Post> findByBoardCodeAndCategoryOrderByCreatedAtDesc(String boardCode, PostCategory category);
List<Post> findByBoardCodeAndCategoryExcludingBlockedUsersOrderByCreatedAtDesc(String boardCode, Long siteUserId, PostCategory category);

서비스 예시:

-        List<Post> postList;
-        if (siteUserId != null) {
-            postList = postRepository.findByBoardCodeExcludingBlockedUsersOrderByCreatedAtDesc(boardCode, siteUserId);
-        } else {
-            postList = postRepository.findByBoardCodeOrderByCreatedAtDesc(boardCode);
-        }
-        return PostListResponse.from(getPostListByPostCategory(postList, postCategory));
+        List<Post> postList =
+            siteUserId != null
+                ? (postCategory == PostCategory.전체
+                    ? postRepository.findByBoardCodeExcludingBlockedUsersOrderByCreatedAtDesc(boardCode, siteUserId)
+                    : postRepository.findByBoardCodeAndCategoryExcludingBlockedUsersOrderByCreatedAtDesc(boardCode, siteUserId, postCategory))
+                : (postCategory == PostCategory.전체
+                    ? postRepository.findByBoardCodeOrderByCreatedAtDesc(boardCode)
+                    : postRepository.findByBoardCodeAndCategoryOrderByCreatedAtDesc(boardCode, postCategory));
+        return PostListResponse.from(postList);

선택: Pageable/Limit을 추가해 상단 n개만 읽는 패턴으로 최적화할 수 있습니다.


93-99: validateCode는 Enum을 직접 반환하는 편이 더 명료합니다.

    1. String.valueOf(BoardCode.valueOf(code)) 대신 BoardCode를 반환하고 .name()은 호출부에서 사용하세요.
    1. 타입 안정성과 오타 방지에 유리합니다.

예시:

-    private String validateCode(String code) {
-        try {
-            return String.valueOf(BoardCode.valueOf(code));
+    private BoardCode validateCode(String code) {
+        try {
+            return BoardCode.valueOf(code);
         } catch (IllegalArgumentException ex) {
             throw new CustomException(INVALID_BOARD_CODE);
         }
     }

호출부:

-    String boardCode = validateCode(code);
-    boardRepository.getByCode(boardCode);
+    BoardCode boardCode = validateCode(code);
+    boardRepository.getByCode(boardCode.name());
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8c100d9 and 6edde9b.

📒 Files selected for processing (12)
  • src/main/java/com/example/solidconnection/common/BaseEntity.java (1 hunks)
  • src/main/java/com/example/solidconnection/community/board/controller/BoardController.java (1 hunks)
  • src/main/java/com/example/solidconnection/community/comment/repository/CommentRepository.java (1 hunks)
  • src/main/java/com/example/solidconnection/community/post/repository/PostRepository.java (1 hunks)
  • src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java (1 hunks)
  • src/test/java/com/example/solidconnection/common/helper/TestTimeHelper.java (1 hunks)
  • src/test/java/com/example/solidconnection/community/comment/fixture/CommentFixture.java (1 hunks)
  • src/test/java/com/example/solidconnection/community/comment/fixture/CommentFixtureBuilder.java (2 hunks)
  • src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java (3 hunks)
  • src/test/java/com/example/solidconnection/community/post/fixture/PostFixture.java (1 hunks)
  • src/test/java/com/example/solidconnection/community/post/fixture/PostFixtureBuilder.java (2 hunks)
  • src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java (5 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/main/java/com/example/solidconnection/common/BaseEntity.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/test/java/com/example/solidconnection/community/post/fixture/PostFixtureBuilder.java
  • src/test/java/com/example/solidconnection/community/post/fixture/PostFixture.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-30T09:16:14.959Z
Learnt from: Gyuhyeok99
PR: solid-connection/solid-connect-server#522
File: src/main/java/com/example/solidconnection/common/BaseEntity.java:39-41
Timestamp: 2025-09-30T09:16:14.959Z
Learning: BaseEntity와 같은 엔티티의 타임스탬프 필드는 테스트를 위해 public setter를 추가하지 말고, reflection이나 EntityManager를 활용한 테스트 전용 헬퍼를 사용해야 합니다. 프로덕션 코드의 캡슐화를 유지하는 것이 중요합니다.

Applied to files:

  • src/test/java/com/example/solidconnection/common/helper/TestTimeHelper.java
🧬 Code graph analysis (1)
src/test/java/com/example/solidconnection/community/comment/fixture/CommentFixtureBuilder.java (1)
src/test/java/com/example/solidconnection/common/helper/TestTimeHelper.java (1)
  • TestTimeHelper (9-19)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (15)
src/test/java/com/example/solidconnection/community/comment/fixture/CommentFixture.java (1)

36-49: LGTM! 시간 지연 테스트를 위한 좋은 헬퍼입니다.

자식_댓글_지연저장 메서드가 기존 패턴과 일관되게 구현되었습니다. secondsDelay 파라미터를 통해 댓글의 생성 시간을 조작하여 정렬 순서 테스트가 가능해졌습니다.

src/main/java/com/example/solidconnection/community/board/controller/BoardController.java (1)

40-40: LGTM! 메서드명이 정렬 의도를 명확히 표현합니다.

findPostsByCodeAndPostCategoryOrderByCreatedAtDesc로 변경하여 최신순 정렬을 메서드명에 명시한 점이 좋습니다. 코드 가독성이 향상되었습니다.

src/test/java/com/example/solidconnection/common/helper/TestTimeHelper.java (1)

10-18: 리플렉션 기반 접근 방식이 적절합니다.

테스트 헬퍼로 리플렉션을 사용하여 createdAt을 조작하는 방식은 프로덕션 코드의 캡슐화를 유지하면서도 테스트에 필요한 유연성을 제공합니다. truncatedTo(ChronoUnit.MICROS)로 데이터베이스 정밀도에 맞춘 점도 좋습니다.

Based on learnings

src/test/java/com/example/solidconnection/community/comment/fixture/CommentFixtureBuilder.java (1)

55-65: 검증 요청: createChildWithDelaySeconds()의 두 번째 save() 동작

  1. 동작 검증
    • 리플렉션으로 변경한 createdAt이 실제로 데이터베이스에 저장되는지 확인.
    • JPA dirty checking이 리플렉션으로 변경된 필드를 감지하는지 확인.
    • @PreUpdatecreatedAt을 덮어쓰지 않고 유지하는지 확인.
  2. 중복 로직 추출 (선택)
    • createChild()createChildWithDelaySeconds() 간 중복된 초기화 코드를 별도 메서드로 분리 고려.
src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java (5)

80-106: 명시적 시간 제어로 테스트 결정성 향상 굿!

    1. 지연저장 파라미터로 createdAt 간극을 확보해 정렬 검증이 안정적입니다.
    1. 동일 밀리초 충돌을 피한 점이 특히 좋습니다.

120-125: 서비스 메서드 교체 적합.

    1. 최신순 메서드로 교체가 목적과 일치합니다.
    1. siteUserId null 케이스도 명확합니다.

220-225: 목록 조회 경로 일관성 유지 OK.

    1. 썸네일 검증 케이스도 최신순 경로를 사용해 일관됩니다.
    1. 데이터 준비-조회-검증 흐름이 간결합니다.

238-243: 이미지 없음 케이스도 최신순 경로로 정리된 점 좋습니다.

    1. 조회 경로 통일로 회귀 위험을 줄였습니다.

264-269: 차단 사용자 제외 검증 흐름 적절.

    1. 차단 로직과 최신순 조회를 함께 검증하는 구성이 깔끔합니다.
src/main/java/com/example/solidconnection/community/post/service/PostQueryService.java (1)

49-62: 레거시 메서드 호출 잔존 여부 점검

  1. 서비스 메서드명 변경 전 호출 확인
    bash  rg -n --type java '\bfindPostsByCodeAndPostCategory\s*\('  
  2. 리포지토리 메서드명 변경 전 호출 확인
    bash  rg -n --type java '\bfindByBoardCodeExcludingBlockedUsers\s*\('  rg -n --type java '\bfindByBoardCode\s*\('  
    출력 결과가 없으면 모든 호출부가 최신 메서드명으로 정리된 상태입니다.
src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java (5)

85-85: 테스트 메서드명 변경 적절합니다.

생성시간 기준 정렬을 명시적으로 표현하여 테스트 의도가 명확해졌습니다.


88-91: 시간차를 둔 댓글 생성으로 정렬 검증을 명확히 했습니다.

자식 댓글들을 3초, 5초 간격으로 생성하여 createdAt 기준 정렬이 제대로 작동하는지 확인할 수 있도록 테스트 데이터를 구성한 점이 좋습니다.


133-141: 정렬 순서 검증 추가 완료 - 이전 리뷰 피드백 반영되었습니다.

containsExactly를 사용하여 응답의 정확한 순서를 검증하도록 개선되었습니다.

  1. 부모 댓글이 최상단에 위치
  2. 자식 댓글들이 생성시간(createdAt) 순서대로 나열

이전 리뷰에서 @Gyuhyeok99님이 요청하신 순서 검증이 정확히 구현되었습니다.


229-233: 차단된 사용자 테스트에 시간차 적용 - 정렬 검증 강화.

차단 사용자 필터링 시나리오에도 시간차를 둔 댓글 생성을 적용하여 정렬 순서가 올바르게 유지되는지 검증할 수 있도록 개선되었습니다.


241-250: 차단 사용자 필터링 후 정렬 순서 검증 완료.

  1. 차단된 사용자의 댓글이 제외되는지 확인(doesNotContain)
  2. 남은 댓글들의 정확한 순서를 검증(containsExactly)
  3. 상대적 순서도 확인(containsSubsequence)

차단 필터링과 정렬이 모두 올바르게 작동하는지 종합적으로 검증하고 있습니다.

)
SELECT * FROM CommentTree
ORDER BY path
ORDER BY path, created_at
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

정렬 방향(ASC/DESC)을 명시해주세요.

ORDER BY path, created_at에서 created_at의 정렬 방향이 명시되지 않아 기본값인 ASC(오래된 순)로 동작합니다. 같은 계층(path) 내에서 최신 댓글을 먼저 보여주려면 ORDER BY path, created_at DESC로 수정해야 하고, 오래된 댓글을 먼저 보여주는 것이 의도라면 명확성을 위해 ORDER BY path, created_at ASC로 명시하는 것을 권장합니다.

다음 스크립트로 PostRepository의 정렬 방향과 일관성을 확인해보세요:


🏁 Script executed:

#!/bin/bash
# Description: PostRepository의 createdAt 정렬 방향 확인 및 CommentRepository와 비교

echo "=== PostRepository의 ORDER BY 절 확인 ==="
rg -n "ORDER BY.*createdAt" src/main/java/com/example/solidconnection/community/post/repository/

echo -e "\n=== CommentRepository의 ORDER BY 절 확인 ==="
rg -n "ORDER BY" src/main/java/com/example/solidconnection/community/comment/repository/

Length of output: 341


정렬 방향을 명시해 주세요.

변경 내용을 한눈에 확인하려면:

  1. PostRepository
    • ORDER BY p.createdAt DESC
  2. CommentRepository
    • ORDER BY path, created_at (기본 ASC)

원하는 순서에 따라 아래처럼 수정하세요:

  • 최신 댓글 우선: ORDER BY path, created_at DESC
  • 오래된 댓글 우선: ORDER BY path, created_at ASC
🤖 Prompt for AI Agents
In
src/main/java/com/example/solidconnection/community/comment/repository/CommentRepository.java
around line 37, the ORDER BY clause currently uses "ORDER BY path, created_at"
without an explicit sort direction; update it to include the desired direction
for created_at (e.g., "path, created_at DESC" for newest-first or "path,
created_at ASC" for oldest-first) so the sorting behavior is explicit and
consistent.

변경된 메서드:
- 게시판_코드와_카테고리로_게시글_목록을_최신순으로_조회한다
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java (1)

138-159: 순서 검증 로직 추가를 권장합니다.

현재 테스트는 usingRecursiveComparison으로 내용만 비교하고 있습니다. 최신순 정렬을 명시적으로 검증하기 위해 ID 순서 확인을 추가하는 것이 좋습니다.

다음과 같이 순서 검증을 추가해보세요:

         // then
-        assertThat(actualResponses)
-                .usingRecursiveComparison()
-                .ignoringFieldsOfTypes(ZonedDateTime.class)
-                .isEqualTo(expectedResponses);
+        assertAll(
+                () -> assertThat(actualResponses)
+                        .usingRecursiveComparison()
+                        .ignoringFieldsOfTypes(ZonedDateTime.class)
+                        .isEqualTo(expectedResponses),
+                () -> assertThat(actualResponses)
+                        .extracting(PostListResponse::id)
+                        .containsExactly(post4.getId(), post3.getId(), post1.getId())
+        );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6edde9b and 58b8bdf.

📒 Files selected for processing (1)
  • src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java (5 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (5)
src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java (5)

69-107: 테스트 데이터 구성이 정렬 검증에 적합합니다.

  1. 시간차 명시 생성

    • 게시글_지연저장을 활용하여 각 포스트에 3초, 6초, 9초의 간격을 두고 생성했습니다.
    • 이를 통해 createdAt이 명확히 구분되어 최신순 정렬 테스트가 안정적으로 동작합니다.
  2. 다양한 게시판/카테고리 조합

    • post1: 자유게시판 + 자유 카테고리
    • post2: 미주권 게시판 + 자유 카테고리
    • post3: 자유게시판 + 질문 카테고리
    • post4: 자유게시판 + 자유 카테고리
    • 필터링 테스트에 필요한 다양한 조합이 잘 준비되었습니다.

110-136: 최신순 정렬 검증이 명확하게 추가되었습니다.

  1. 테스트명 개선

    • 메서드명에 "최신순으로_조회한다"를 추가하여 테스트 의도가 명확해졌습니다.
  2. 순서 검증 로직 추가

    • assertAll에서 내용 비교와 함께 ID 순서를 명시적으로 검증합니다 (lines 132-134).
    • 필터링 후 예상되는 post4 → post1 순서가 정확히 검증됩니다.
  3. 서비스 메서드명 변경 반영

    • findPostsByCodeAndPostCategoryOrderByCreatedAtDesc로 메서드명이 변경되어 정렬 기준이 명확합니다.

211-233: 서비스 메서드명 변경이 반영되었습니다.

findPostsByCodeAndPostCategoryOrderByCreatedAtDesc로 메서드명이 변경되어 정렬 기준이 명확해졌습니다. 썸네일 로직은 정렬 순서와 무관하게 정상 동작합니다.


235-251: 서비스 메서드명 변경이 반영되었습니다.

findPostsByCodeAndPostCategoryOrderByCreatedAtDesc로 메서드명이 변경되어 정렬 기준이 명확해졌습니다.


253-275: 서비스 메서드명 변경이 반영되었습니다.

findPostsByCodeAndPostCategoryOrderByCreatedAtDesc로 메서드명이 변경되어 정렬 기준이 명확해졌습니다. 차단 사용자 필터링 로직은 정상 동작합니다.

Copy link
Member

@whqtker whqtker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정말 고생하셨습니다 ~ 중복 검증 관련해서 코멘트 남겼는데, 의견 편하게 남겨주시면 됩니다 !
기존 레거시에 대한 리뷰도 들어갔는데요 .. 이 PR에 반영해도 적합하다고 생각하시면 반영해주세요 ~

import jakarta.persistence.PrePersist;
import jakarta.persistence.PreUpdate;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

미사용 import문 제거해주세요 ! 인텔리제이 사용하시면 설정을 통해 자동 삭제 가능합니다.

Copy link
Contributor

@Gyuhyeok99 Gyuhyeok99 Oct 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

참고로 reformat code 적용해도 알아서 제거 해주긴 합니다~

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 이런 편리한 기능이... 실수할 일이 적어질 것 같네요! 감사합니다~

// when
List<PostFindCommentResponse> responses = commentService.findCommentsByPostId(user1.getId(), post.getId());

System.out.println(responses);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기 디버깅문 있습니다 !

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이전 커밋들에서도 계속 이걸 작성하고 제거하신 거 같은데요!
이렇게 직접 찍는 거 대신 중단점 한 번 습관화 해보시는 거 어떤가요!?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 중단점 습관화를 한 번 도전해볼게요!


// then
assertAll(
() -> assertThat(responses).hasSize(comments.size()),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

마지막에 containsExactly 로 조회된 댓글 수 검증을 진행하므로 해당 검증은 중복이라 없어도 될 거 같습니다 !

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 반영하겠습니다!

.singleElement()
.satisfies(response -> assertAll(
() -> assertThat(response.id()).isEqualTo(childComment.getId()),
() -> assertThat(response.id()).isEqualTo(childComment1.getId()),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

레거시 코드 같긴 하지만, filteredOn 에서 id 검증이 이미 이루어졌다고 생각합니다 ! 따라서 satisfies 내부에 isEqualTo 는 중복 검증이라고 생각합니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 확인했습니다!


Post saved = postRepository.save(post);

System.out.println(saved);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기도 디버깅 있습니다 !

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 확인했습니다!

public Comment createChildWithDelaySeconds(long seconds) {
Comment comment = new Comment(content);
comment.setPostAndSiteUserId(post, siteUser.getId());
comment.setParentCommentAndPostAndSiteUserId(parentComment, post, siteUser.getId());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어라 이거도 레거시같은데 setParentCommentAndPostAndSiteUserId 동작이 setPostAndSiteUserId 동작을 포함하네요 ... 여기 PR에 반영하는 게 나을까요 따로 PR 파는 게 나을까요 ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서 같이 제거해도 될 거 같습니다!

7월에 반영된 거 같은데 놓쳤네요..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 반영하겠습니다!

제거된 메서드:
- setPostAndSiteUserId
제거된 검증:
- filteredOn에 의해 검증된 comment.Id 값을 다시 한 번 검증하는 isEqualTo(comment.getId())로직
Copy link
Contributor

@Gyuhyeok99 Gyuhyeok99 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

자꾸 코멘트 달려서 힘드시죠..? 이게 정말 마지막일 거 같습니다! 고생 많으십니다..

차단한_사용자의_게시글은_제외된다 에서만 현재 새로 수정해주신 findByBoardCodeExcludingBlockedUsersOrderByCreatedAtDesc 이 함수를 테스트하고 있습니다! 그래서 추가해주신 OrderByCreatedAtDesc 가 정상작동하는지 추가 검증을 요청드린 것이었습니다!

예를 들어 차단한_사용자의_게시글은_제외하고_게시글_목록을_최신순으로_조회한다
이런 거요! 아니면 별도로 하나 더 만들어두 되구요!

추가로 변경되거나 새로만든 파일들에 대해서는 reformat code를 적용시켜야하는데 이게아직 안된 거 같습니다!

위 테스트 코드 반영되면 바로 approve하겠습니다~

import com.example.solidconnection.community.comment.dto.CommentUpdateRequest;
import com.example.solidconnection.community.comment.dto.CommentUpdateResponse;
import com.example.solidconnection.community.comment.dto.PostFindCommentResponse;
import com.example.solidconnection.community.comment.dto.*;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저희는 와일드카드 사용을 지양하고 있습니다!

컨벤션
여기 들어가서 적용해두시면 와일드카드 사용 안되실 겁니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 주의하고 있었는데, 인텔리제이 기본 설정에 그렇게 변하게 되도록 되어 있네요!! 반영하겠습니다!


import java.util.List;

import static com.example.solidconnection.common.exception.ErrorCode.*;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

마찬가지입니다~

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영하겠습니다!

() -> assertThat(response.isOwner()).isFalse()
)),
() -> assertThat(responses)
.extracting("id")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 예시를 너무 간단히 드렸던거긴 한데.. 지금 와서 보니

as is

.extracting("id")

to be

extracting(PostFindCommentResponse::id)

이게 조금더 나을 거 같기도 하네요! 반영 안해주셔도 괜찮습니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

반영하겠습니다!

Copy link
Member

@whqtker whqtker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다 ~ 😊 다른 분들 리뷰 리졸브하시고 스쿼시 머지 하시면 됩니다 !!

추가된 메서드:
- 차단한_사용자의_게시글은_제외하고_게시글_목록을_최신순으로_조회한다
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java (2)

85-85: 테스트 메서드명 개선 제안

현재 메서드명은 "생성시간 기준으로 정렬"만 언급하고 있지만, 실제 CommentRepository의 정렬 기준은 path 우선, 그 다음 createdAt입니다.

같은 부모 아래 자식 댓글들만 테스트하므로 현재 이름도 틀리지는 않지만, 더 정확한 표현을 고려해보세요:

  • 게시글의_모든_댓글과_대댓글을_계층과_생성시간_기준으로_정렬해_조회한다
  • 또는 게시글의_댓글_트리를_올바른_순서로_조회한다

240-242: 중복 검증 제거 제안

Line 236에서 containsExactly(parentComment1.getId(), childComment1.getId(), childComment3.getId())로 이미 정확한 순서를 검증하고 있습니다.

Line 242의 containsSubsequence는 중복 검증이므로 제거해도 됩니다.

                    () -> assertThat(responses)
                            .extracting(PostFindCommentResponse::id)
                            .doesNotContain(childComment2.getId(), parentComment2.getId(), childComment4.getId(), childComment5.getId()),
-                    () -> assertThat(responses)
-                            .extracting(PostFindCommentResponse::id)
-                            .containsSubsequence(parentComment1.getId(), childComment1.getId(), childComment3.getId())
            );
src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java (1)

138-159: 순서 검증 로직을 추가하면 더 완벽한 테스트가 됩니다

현재 구현:

  • 데이터 내용은 usingRecursiveComparison으로 검증하고 있습니다
  • 하지만 최신순 정렬을 명시적으로 검증하는 로직이 없습니다

이전 테스트(lines 127-135)처럼 containsExactly를 사용한 순서 검증을 추가해주세요.

다음과 같이 개선할 수 있습니다:

     // then
-    assertThat(actualResponses)
-            .usingRecursiveComparison()
-            .ignoringFieldsOfTypes(ZonedDateTime.class)
-            .isEqualTo(expectedResponses);
+    assertAll(
+            () -> assertThat(actualResponses)
+                    .usingRecursiveComparison()
+                    .ignoringFieldsOfTypes(ZonedDateTime.class)
+                    .isEqualTo(expectedResponses),
+            () -> assertThat(actualResponses)
+                    .extracting(PostListResponse::id)
+                    .containsExactly(post4.getId(), post3.getId(), post1.getId())
+    );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e193241 and eb7c63c.

📒 Files selected for processing (2)
  • src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java (2 hunks)
  • src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java (6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (7)
src/test/java/com/example/solidconnection/community/comment/service/CommentServiceTest.java (2)

87-91: 테스트 데이터 설정이 적절합니다

  1. 부모 댓글과 자식 댓글들을 지연 저장으로 생성하여 createdAt 차이를 명확하게 만들었습니다.
  2. 예상 순서(parent → child1 → child2 → child3)가 명확하게 정의되었습니다.

126-133: 정렬 순서 검증이 정확하게 추가되었습니다

  1. extracting(PostFindCommentResponse::id)로 메서드 레퍼런스를 사용했습니다(이전 리뷰 피드백 반영).
  2. containsExactly로 정확한 순서를 검증하여 PR의 핵심 목표인 정렬 순서 수정을 확인합니다.
src/test/java/com/example/solidconnection/community/post/service/PostQueryServiceTest.java (5)

67-106: 테스트 데이터 설정이 명확하게 개선되었습니다

변경 내용:

  1. post4 필드를 추가하여 테스트 데이터를 확장했습니다
  2. 게시글_지연저장 메서드를 사용해 각 게시글에 명시적인 시간 간격(3초, 6초, 9초)을 부여했습니다
    • post1 (기준 시간)
    • post2 (+3초)
    • post3 (+6초)
    • post4 (+9초, 최신)
  3. 이를 통해 createdAt DESC 정렬 검증을 위한 결정적(deterministic) 순서를 보장합니다

이전 리뷰에서 지적된 타임스탬프 충돌 문제가 완전히 해결되었습니다.


110-136: 정렬 검증 로직이 정확하게 구현되었습니다

개선 사항:

  1. 테스트 메서드명에 "최신순으로"를 명시하여 의도를 명확히 했습니다
  2. assertAll을 사용해 두 가지 검증을 수행합니다:
    • 데이터 내용 비교 (ZonedDateTime 필드 제외)
    • 순서 검증: containsExactly(post4.getId(), post1.getId())
  3. 필터링 로직(자유 카테고리 + FREE 게시판)과 정렬 순서(최신순)를 모두 정확하게 검증합니다

이전 리뷰 피드백이 완전히 반영되었습니다.


212-251: 썸네일 테스트가 새로운 메서드명으로 올바르게 업데이트되었습니다

변경 사항:

  1. 서비스 메서드 호출을 findPostsByCodeAndPostCategoryOrderByCreatedAtDesc로 변경했습니다 (lines 220, 238)
  2. 썸네일 로직 검증은 정렬 순서와 무관하게 정상 작동합니다
  3. 테스트 의도가 명확하게 유지되고 있습니다

253-275: 차단 사용자 제외 기능이 올바르게 검증되고 있습니다

변경 사항:

  1. 서비스 메서드를 findPostsByCodeAndPostCategoryOrderByCreatedAtDesc로 업데이트했습니다 (line 264)
  2. 차단되지 않은 사용자의 게시글 포함 여부를 검증합니다
  3. 차단된 사용자의 게시글 제외 여부를 검증합니다

참고: 순서 검증은 lines 277-307의 새로운 테스트에서 별도로 다루고 있습니다.


277-307: 차단 기능과 정렬 순서를 함께 검증하는 포괄적인 테스트가 추가되었습니다

테스트의 강점:

  1. 차단된 사용자 제외와 createdAt DESC 정렬을 동시에 검증합니다
  2. containsExactlyElementsOf를 사용해 정확한 순서를 검증합니다:
    • post4 (최신)
    • post3
    • post1 (가장 오래됨)
  3. doesNotContain으로 차단된 게시글의 제외를 명시적으로 확인합니다
  4. 기존 테스트(lines 253-275)와 중복되지 않으면서 순서 검증을 추가하여 커버리지를 강화했습니다

이전 리뷰 피드백(순서 검증 추가)이 완벽하게 반영되었습니다.

Copy link
Contributor

@Gyuhyeok99 Gyuhyeok99 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다~ Approve했습니다!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/test/java/com/example/solidconnection/common/helper/TestTimeHelper.java (3)

8-8: 유틸리티 클래스는 final로 선언하고 private 생성자를 추가하세요.

정적 메서드만 제공하는 유틸리티 클래스는 인스턴스화를 방지하기 위해 final로 선언하고 private 생성자를 추가하는 것이 Java 모범 사례입니다.

다음 diff를 적용하세요:

-public class TestTimeHelper {
+public final class TestTimeHelper {
+
+    private TestTimeHelper() {
+        throw new UnsupportedOperationException("Utility class");
+    }
 
     public static void setCreatedAt(BaseEntity entity, ZonedDateTime time) {

10-10: 파라미터 null 검증을 추가하세요.

entitytime 파라미터에 대한 null 검증이 없어서, null이 전달되면 NullPointerException이 발생할 수 있습니다.

다음 diff를 적용하여 null 검증을 추가하세요:

     public static void setCreatedAt(BaseEntity entity, ZonedDateTime time) {
+        if (entity == null || time == null) {
+            throw new IllegalArgumentException("Entity and time must not be null");
+        }
         try {

15-17: 예외 처리를 구체화하고 에러 메시지를 개선하세요.

현재 구현의 개선점:

  1. 모든 예외를 Exception으로 포괄적으로 잡는 대신, 구체적인 reflection 예외(NoSuchFieldException, IllegalAccessException)를 명시하세요.
  2. 에러 메시지에 어떤 문제가 발생했는지 컨텍스트를 추가하세요.

다음 diff를 적용하세요:

-        } catch (Exception e) {
-            throw new RuntimeException(e);
+        } catch (NoSuchFieldException | IllegalAccessException e) {
+            throw new RuntimeException("Failed to set createdAt field on BaseEntity", e);
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb7c63c and 774e3b2.

📒 Files selected for processing (3)
  • src/main/java/com/example/solidconnection/community/comment/repository/CommentRepository.java (1 hunks)
  • src/main/java/com/example/solidconnection/community/post/repository/PostRepository.java (1 hunks)
  • src/test/java/com/example/solidconnection/common/helper/TestTimeHelper.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/example/solidconnection/community/comment/repository/CommentRepository.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-30T09:16:14.959Z
Learnt from: Gyuhyeok99
PR: solid-connection/solid-connect-server#522
File: src/main/java/com/example/solidconnection/common/BaseEntity.java:39-41
Timestamp: 2025-09-30T09:16:14.959Z
Learning: BaseEntity와 같은 엔티티의 타임스탬프 필드는 테스트를 위해 public setter를 추가하지 말고, reflection이나 EntityManager를 활용한 테스트 전용 헬퍼를 사용해야 합니다. 프로덕션 코드의 캡슐화를 유지하는 것이 중요합니다.

Applied to files:

  • src/test/java/com/example/solidconnection/common/helper/TestTimeHelper.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (3)
src/main/java/com/example/solidconnection/community/post/repository/PostRepository.java (2)

17-17: 1. 메서드명 변경을 통한 정렬 순서 명시화 완료

findByBoardCode에서 findByBoardCodeOrderByCreatedAtDesc로 변경하여 Spring Data JPA의 네이밍 컨벤션을 활용한 정렬 순서 명시가 잘 적용되었습니다. 최신 게시글이 최상단에 노출되도록 하는 PR 목표와 일치합니다.

다만 PR 설명에서 언급하신 "메서드명 길이"에 대한 의견을 드리자면, OrderByCreatedAtDesc 접미사가 다소 길게 느껴질 수 있으나 Spring Data JPA의 표준 네이밍 규칙을 따르고 있어 가독성과 명확성 측면에서는 적절한 선택입니다.


19-27: 2. 차단 사용자 제외 쿼리에 정렬 순서 추가 완료

변경 내용을 정리하면:

  • 쿼리 정렬 추가: ORDER BY p.createdAt DESC 절을 명시적으로 추가하여 최신 게시글 우선 정렬 보장
  • 메서드명 개명: findByBoardCodeExcludingBlockedUsersfindByBoardCodeExcludingBlockedUsersOrderByCreatedAtDesc로 변경하여 정렬 의도를 메서드명에 명확히 반영

JPQL 구문이 정확하며, 서비스 레이어 및 테스트 코드와의 연동도 AI 요약에서 확인된 바와 같이 일관되게 처리되었습니다.

src/test/java/com/example/solidconnection/common/helper/TestTimeHelper.java (1)

10-18: Reflection 기반 접근 방식이 올바르게 적용되었습니다.

이 헬퍼는 학습된 가이드라인에 따라 BaseEntity에 public setter를 추가하지 않고 reflection을 사용하여 테스트에서 createdAt 필드를 설정합니다. 프로덕션 코드의 캡슐화를 유지하면서 테스트 요구사항을 충족하는 적절한 구현입니다.

Based on learnings

@Hexeong Hexeong merged commit 27fd21b into solid-connection:develop Oct 2, 2025
2 checks passed
Gyuhyeok99 added a commit that referenced this pull request Nov 4, 2025
fix: PostRepository & CommentRepository의 일부 메서드의 정렬 순서 오류 해결 (#522) 
refactor: 이메일 유니크키 제거
refactor: 엔티티가 BaseEntity를 상속하도록 (#524) 
fix: BaseEntity 관련 마이그레이션 파일 수정 (#530) 
feat: 멘토 승격 api 구현 (#532) 
fix: 대학교 중복 오류 수정 (#510) 
refactor: 불필요한 로그 삭제 (#543)
fix: 운영환경 8081 포트 설정 추가 (#542) 
fix: dev환경 디비명 변경 (#546)
refactor: 불필요한 로그 삭제 (#547) 
refactor: 학기를 테이블로 관리하도록 변경 (#526) 
refactor: 모의지원 시 지원한 대학 정보 응답 추가 (#539)
@Gyuhyeok99 Gyuhyeok99 mentioned this pull request Nov 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

버그 Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: 커뮤니티 도메인 내의 PostRepository & CommentRepository의 일부 메서드의 정렬 순서 오류

3 participants